package com.example.ploy4j.service.impl;

import com.example.ploy4j.dto.GitRepositoryConfigDto;
import com.example.ploy4j.dto.mapper.GitRepositoryConfigMapper;
import com.example.ploy4j.entity.GitRepositoryConfig;
import com.example.ploy4j.repository.GitRepositoryConfigRepository;
import com.example.ploy4j.service.GitRepositoryConfigService;
import jakarta.persistence.EntityNotFoundException;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

// JGit for Git connection testing
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.LsRemoteCommand;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider;
import org.eclipse.jgit.lib.Ref;

// Import new dependencies
import com.example.ploy4j.entity.DeploymentJob;
import com.example.ploy4j.entity.DeploymentStatus;
import com.example.ploy4j.repository.DeploymentJobRepository;

@Service
@RequiredArgsConstructor
public class GitRepositoryConfigServiceImpl implements GitRepositoryConfigService {

    private final GitRepositoryConfigRepository gitRepositoryConfigRepository;
    private final GitRepositoryConfigMapper gitRepositoryConfigMapper; // 注入Mapper
    private final DeploymentJobRepository deploymentJobRepository; // 新增注入

    @Override
    @Transactional
    public GitRepositoryConfigDto createGitRepositoryConfig(GitRepositoryConfigDto gitRepositoryConfigDto) {
        GitRepositoryConfig gitRepositoryConfig = gitRepositoryConfigMapper.toEntity(gitRepositoryConfigDto);
        GitRepositoryConfig savedConfig = gitRepositoryConfigRepository.save(gitRepositoryConfig);
        return gitRepositoryConfigMapper.toDto(savedConfig);
    }

    @Override
    @Transactional(readOnly = true)
    public Optional<GitRepositoryConfigDto> getGitRepositoryConfigById(Long id) {
        return gitRepositoryConfigRepository.findById(id)
                .map(gitRepositoryConfigMapper::toDto);
    }

//    @Override
//    public Optional<GitRepositoryConfigDto> getGitRepositoryConfigByIp(String ip) {
//        return gitRepositoryConfigRepository.findByIp(ip)
//                .map(gitRepositoryConfigMapper::toDto);
//    }

    @Override
    @Transactional(readOnly = true)
    public Optional<GitRepositoryConfigDto> getGitRepositoryConfigByProjectName(String projectName) {
        return gitRepositoryConfigRepository.findByProjectName(projectName)
                .map(gitRepositoryConfigMapper::toDto);
    }


    @Override
    @Transactional(readOnly = true)
    public List<GitRepositoryConfigDto> getAllGitRepositoryConfigs() {
        List<GitRepositoryConfig> configs = gitRepositoryConfigRepository.findAll();
        List<GitRepositoryConfigDto> dtos = gitRepositoryConfigMapper.toDtoList(configs);

        // For each DTO, find the last successful deployment time
        dtos.forEach(dto -> {
            // Need the original entity to query DeploymentJobRepository as it expects GitRepositoryConfig, not just ID
            Optional<GitRepositoryConfig> originalConfigOptional = configs.stream()
                .filter(c -> c.getId().equals(dto.getId()))
                .findFirst();

            if (originalConfigOptional.isPresent()) {
                Optional<DeploymentJob> lastSuccessfulJob = deploymentJobRepository
                    .findTopByGitRepositoryConfigAndStatusOrderByCompletedAtDesc(
                        originalConfigOptional.get(), 
                        DeploymentStatus.SUCCESS
                    );
                lastSuccessfulJob.ifPresent(job -> dto.setLastSuccessfulDeploymentAt(job.getCompletedAt()));
            }
        });

        return dtos;
    }

    @Override
    @Transactional
    public GitRepositoryConfigDto updateGitRepositoryConfig(Long id, GitRepositoryConfigDto gitRepositoryConfigDto) {
        GitRepositoryConfig existingConfig = gitRepositoryConfigRepository.findById(id)
                .orElseThrow(() -> new EntityNotFoundException("未找到ID为: " + id + " 的Git仓库配置"));

        gitRepositoryConfigMapper.updateEntityFromDto(gitRepositoryConfigDto, existingConfig);
        // 对于Gitee Token的处理与ServerConfig中的密码类似，依赖WRITE_ONLY和MapStruct的更新策略

        GitRepositoryConfig updatedConfig = gitRepositoryConfigRepository.save(existingConfig);
        return gitRepositoryConfigMapper.toDto(updatedConfig);
    }

    @Override
    @Transactional
    public void deleteGitRepositoryConfig(Long id) {
        if (!gitRepositoryConfigRepository.existsById(id)) {
            throw new EntityNotFoundException("未找到ID为: " + id + " 的Git仓库配置，无法删除");
        }
        gitRepositoryConfigRepository.deleteById(id);
    }

    @Override
    @Transactional(readOnly = true)
    public Map<String, Object> testConnection(Long id) {
        GitRepositoryConfig gitRepoConfig = gitRepositoryConfigRepository.findById(id)
                .orElseThrow(() -> new EntityNotFoundException("未找到ID为: " + id + " 的Git仓库配置，无法测试连接"));

        Map<String, Object> response = new HashMap<>();
        LsRemoteCommand lsRemoteCommand = Git.lsRemoteRepository()
                .setRemote(gitRepoConfig.getRepositoryUrl())
                .setHeads(true) // Check heads
                .setTags(true); // Check tags

        // Handle authentication if Gitee token is provided
        // JGit typically handles basic auth via URL or credential provider for HTTP/S
        // For Gitee token with HTTPS, it often acts as the password with a generic username (e.g., "oauth2" or actual Gitee username)
        if (gitRepoConfig.getGiteeToken() != null && !gitRepoConfig.getGiteeToken().isBlank()) {
            if (gitRepoConfig.getRepositoryUrl().startsWith("https")) {
                 // For Gitee, token can be used as password. Username can be anything or specific if Gitee requires.
                 // A common practice is to use "oauth2" or the actual Gitee username.
                 // Let's assume the token itself is sufficient as a password for ls-remote via HTTPS.
                 // JGit might also pick up .git-credentials, but explicit is better here.
                lsRemoteCommand.setCredentialsProvider(new UsernamePasswordCredentialsProvider("PRIVATE-TOKEN", gitRepoConfig.getGiteeToken()));
                // Alternative for Gitee if token is Bearer type (less common for ls-remote directly)
                // For simple token auth (like GitHub personal access tokens used as passwords), this is typical.
            }
            // SSH key based auth would require a different setup with JschConfigSessionFactory if repo URL is SSH
        }

        try {
            Collection<Ref> refs = lsRemoteCommand.call();
            if (refs != null && !refs.isEmpty()) {
                response.put("success", true);
                response.put("message", "成功连接到仓库 " + gitRepoConfig.getRepositoryUrl() + " 并获取到 " + refs.size() + " 个引用。");
            } else {
                // This might occur if repo is empty or no heads/tags, but connection was successful
                response.put("success", true); // Still a success in terms of connectivity
                response.put("message", "成功连接到仓库 " + gitRepoConfig.getRepositoryUrl() + " 但未发现任何引用 (heads/tags)。仓库可能为空。 ");
            }
        } catch (GitAPIException e) {
            response.put("success", false);
            response.put("message", "测试Git仓库连接失败: " + e.getMessage());
            // Log e for server-side details
        } catch (Exception e) { // Catch other potential runtime exceptions
            response.put("success", false);
            response.put("message", "测试Git仓库连接时发生意外错误: " + e.getMessage());
        }
        return response;
    }
} 